using System;
using System.ComponentModel;
using Microsoft.Practices.CompositeWeb.Interfaces;
using WCSFContrib.Extensions.CompositeWeb.Interfaces;

namespace WCSFContrib.Extensions.CompositeWeb.Web
{
    /// <summary>
    /// Implements the <see cref="IQueryStringValue"/> interface for a generic type.
    /// </summary>
    /// <typeparam name="T">The type of the value to manage.</typeparam>
    public class QueryStringValue<T> : IQueryStringValue
    {
        // Fields
        private string _keyName;
        private IHttpContext _httpContext;
        private object _value;

        /// <summary>
        /// Initializes a new instance of <see cref="QueryStringValue{T}"/>.
        /// </summary>
        public QueryStringValue()
        {
            this._value = default(T);
        }

        /// <summary>
        /// Initializes a new instance of <see cref="QueryStringValue{T}"/>.
        /// </summary>
        /// <param name="value">The initial value for the field.</param>
        public QueryStringValue(T value)
        {
            this._value = value;
        }

        /// <summary>
        /// Gets or sets the HttpContext.
        /// </summary>
        public IHttpContext HttpContext
        {
            get
            {
                return this._httpContext;
            }
            set
            {
                this._httpContext = value;
            }
        }

        /// <summary>
        /// Gets or sets the key of the value in the QueryString.
        /// </summary>
        public string KeyName
        {
            get
            {
                return this._keyName;
            }
            set
            {
                this._keyName = value;
            }
        }

        /// <summary>
        /// Sets the default value.
        /// </summary>
        public void SetDefault()
        {
            this._value = default(T);
        }

        /// <summary>
        /// Gets or sets the actual value from the QueryString.
        /// </summary>
        public T Value
        {
            get
            {
                object value = ((IQueryStringValue)this).Value;
                Type type = typeof(T);

                if (!IsNullableType(type))
                {
                    return (T)Convert.ChangeType(value, type);
                }
                else
                {
                    if (value == null)
                        return (T)value;

                    NullableConverter nc = new NullableConverter(type);
                    Type underlyingType = nc.UnderlyingType;

                    return (T)Convert.ChangeType(value, underlyingType);
                }
            }
        }

        /// <summary>
        /// Gets or sets the actual value from the QueryString.
        /// </summary>
        object IQueryStringValue.Value
        {
            get
            {
                if ((this == null) || (this.HttpContext == null) || (this.HttpContext.Request.QueryString[this._keyName] == null))
                {
                    return this._value;
                }
                return this.HttpContext.Request.QueryString[this._keyName];
            }
        }

        private static bool IsNullableType(Type theType)
        {
            return (theType.IsGenericType && theType.GetGenericTypeDefinition().Equals(typeof(Nullable<>)));
        }
    }
}